package test

import (
	"bytes"
	"encoding/binary"
	"io"

	"gitee.com/sdxstar/air/protocol"
	"github.com/pkg/errors"
)

type myProtocol struct {
	cryptor *MyCryptor
}

func NewProtocol() protocol.Protocol {
	return &myProtocol{cryptor: NewCryptor([]byte("XYSDX123"))}
}

type packet struct {
	key   uint16
	id    uint64
	event []byte
	body  []byte
}

func (dp *myProtocol) NewPacket(key protocol.MessageKey, id uint64, event string, data []byte) protocol.Packet {
	return &packet{
		key:   key,
		id:    id,
		event: []byte(event),
		body:  data,
	}
}

func (p *packet) NewReplyPacket(data []byte) protocol.Packet {
	return &packet{
		key:  protocol.MessageKeyAckResponse,
		id:   p.id,
		body: data,
	}
}

func (p *packet) Key() uint16 {
	return p.key
}

func (p *packet) Id() uint64 {
	return p.id
}

func (p *packet) Event() string {
	return string(p.event)
}

func (p *packet) Body() []byte {
	return p.body
}

func (dp *myProtocol) WritePacket(pack protocol.Packet, w io.Writer) (int, error) {
	p := pack.(*packet)
	event, err := dp.cryptor.Encrypt(p.event)
	if err != nil {
		return 0, errors.Wrap(err, "encrypt event failed")
	}
	eventSize := len(event)
	body, err := dp.cryptor.Encrypt(p.body)
	if err != nil {
		return 0, errors.Wrap(err, "encrypt body failed")
	}
	size := len(body)
	buf := make([]byte, 18)
	buffer := bytes.NewBuffer([]byte{})
	binary.BigEndian.PutUint16(buf[:2], p.key)
	binary.BigEndian.PutUint64(buf[2:10], p.id)
	binary.BigEndian.PutUint32(buf[10:14], uint32(eventSize))
	binary.BigEndian.PutUint32(buf[14:18], uint32(size))
	buffer.Write(buf)
	buffer.Write(event)
	buffer.Write(body)
	return w.Write(buffer.Bytes())
}

func (dp *myProtocol) ReadPacket(r io.Reader) (protocol.Packet, error) {
	var p packet
	var eventSize uint32
	var size uint32
	err := binary.Read(r, binary.BigEndian, &p.key)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &p.id)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &eventSize)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &size)
	if err != nil {
		return nil, err
	}
	if eventSize > 0 {
		buf := make([]byte, eventSize)
		_, err = io.ReadFull(r, buf)
		if err != nil {
			return nil, errors.Wrap(err, "read packet event size failed")
		}
		event, err := dp.cryptor.Decrypt(buf)
		if err != nil {
			return nil, errors.Wrap(err, "decrypt event failed")
		}
		p.event = event
	}
	if size > 0 {
		buf := make([]byte, size)
		_, err = io.ReadFull(r, buf)
		if err != nil {
			return nil, errors.Wrap(err, "read packet data size failed")
		}
		body, err := dp.cryptor.Decrypt(buf)
		if err != nil {
			return nil, errors.Wrap(err, "decrypt body failed")
		}
		p.body = body
	}
	return &p, nil
}
